import re
import time
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
import gevent
import traceback
from .matter_enum import *


def msg_parser(dispatcher, ui, ssh_ch, cm_step, msg):
    _cluster_id, _endpoint, _atrrib_id, _attrib_name, _cmd_id, _cmd_name = None, None, None, None, None, None
    _status_code, _src, _c_step, _cmd_send, _commission_step = None, None, None, None, cm_step
    # 1. Onboarding payload paser
    # 1.1 OCW payload
    if '==== Onboarding payload for Standard Commissioning Flow ====' in msg:
        _qrcode = re.findall(
            'Onboarding payload for Standard Commissioning Flow ====\r\n.*SetupQRCode: \[(.*)\]\r', msg)
        _manual_pair_code = re.findall('Manual pairing code: \[(.*)\]', msg)
        if _qrcode and _manual_pair_code:
            dispatcher.put((ui.fabric_payload, 'QLineEdit.setText', _manual_pair_code[0]))
            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                        f'\nOCW Successful\n'
                        f'\tManual pairing code: {_manual_pair_code[0]}\n'
                        f'\tSetupQRCode:{_qrcode[0]}'))
    # 1.2 Payload(SetupQRCode， ManualPairingCode) parser
    if 'Parsing base38Representation' in msg:
        _passcode = re.findall(r'Passcode:            (.*)\r', msg)
        _long_disc = re.findall(r'Long discriminator:  (.*)   \(', msg)
        if _passcode and _long_disc:
            dispatcher.put((ui.setup_code, 'QLineEdit.setText', _passcode[0]))
            dispatcher.put((ui.discriminator, 'QLineEdit.setText', _long_disc[0]))
            dispatcher.put((ui.node_id_assign, 'QLineEdit.setText', _long_disc[0]))
    # 2. OTBR
    #if 'Done' in msg and self.current_cmd == 'otbr_dataset_active':
    if 'Done' in msg:
        _dataset = re.findall('(.*)Done', msg, re.DOTALL)
        if len(_dataset) > 0:
            dispatcher.put((ui.operational_dataset, 'QLineEdit.setText', _dataset[0].strip()))
    if 'otbr-agent.service - OpenThread Border Router Agent' in msg:
        _state = re.findall('otbr-agent.service - OpenThread Border Router Agent.*', msg, re.DOTALL)
        if len(_state) > 0:
            if 'active (running)' in _state[0]:
                dispatcher.put((ui.otbr_agent_state, 'QLineEdit.setText', 'otbr-agent Active'))
                # ui.otbr_agent_state.setStyleSheet('background-color: rgb(124,252,0);')
    if 'otbr-web.service - Border Router Web' in msg:
        _state = re.findall('otbr-web.service - Border Router Web.*', msg, re.DOTALL)
        if len(_state) > 0:
            if ' Active: active (running)' in _state[0]:
                dispatcher.put((ui.otbr_web_status, 'QLineEdit.setText', 'otbr-web Active'))
                # ui.otbr_web_status.setStyleSheet('background-color: rgb(124,252,0);')
    if 'mdns.service' in msg:
        _state = re.findall('mdns.service.*', msg, re.DOTALL)
        if len(_state) > 0:
            if 'Active: active (running)' in _state[0]:
                dispatcher.put((ui.mdns_status, 'QLineEdit.setText', 'mdns Active'))
                # ui.mdns_status.setStyleSheet('background-color: rgb(124,252,0);')
    # 3. OTA file
    """
    if (self.ota_queried is False) and ('.bin' in msg):
        #command: 
            #ls /home/will/apps/ota_path/
        #Response: (payload is the partment between >>> and <<<<)
            #>>>matter-ota.bin
            #will@matterCenter:~$ <<<<
        _query_res = re.findall('(.*)\r', msg)
        if _query_res:
            dispatcher.put((ui.ota_file_server, 'QLineEdit.setText', _query_res[0]))
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'ota_bin_file:{self.ota_bin_file}'))
            """

    # 4. Commissioning Steps Keywords
    # Receiving msg
    _rx_from = re.findall('Msg RX from (.*)\[.*\(IM:', msg)
    _im_type = re.findall('Msg RX from .*\(IM:(.*)\)\r', msg)
    if len(_rx_from) > 0 and len(_im_type) > 0:
        _src = _rx_from[0]
        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                        f'\n-->> Msg RX from {_rx_from[0]} IM:{_im_type[0]}'))
    # 4.1 BLE scan
    if 'BLE initiating scan' in msg:
        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                    '\n<<- Pairing ble-wifi device ->>\n\t-> BLE initiating scan'))
    # # Discriminator match
    if 'Device discriminator match. Attempting to connect' in msg:
        _dev = re.findall('New device scanned: (.*)\r\n.*Device discriminator match. Attempting ', msg)
        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                    f'--> New device scanned: {_dev[0]} '
                    f'Device discriminator match.\n\t-> Attempting to connect...'))
    if 'Scan complete. No matching device found' in msg:
        dispatcher.put((ui.keynote, 'QTextBrowser.append', '-->Scan complete \n\t->No matching device found'))
    if "Failed to verify peer's MAC. This can happen when setup code is incorrect" in msg:
        dispatcher.put((ui.keynote, 'QTextBrowser.append', '-->SetupCode may be incorrect'))

    # # TX cluster command Sending, not only for Commissioning steps
    _cluster_tx = re.findall('Sending cluster \((.*)\) command \(.*\) on endpoint .*\r', msg)
    _cmd_tx = re.findall('Sending cluster \(.*\) command \((.*)\) on endpoint .*\r', msg)
    _ep_tx = re.findall('Sending cluster \(.*\) command \(.*\) on endpoint (.*)\r', msg)
    _dst_tx = re.findall('Msg TX to (.*) \[.*IM:InvokeCommandRequest\)\r', msg)
    if len(_dst_tx) and len(_cluster_tx):
        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                        f'<<-- Msg TX to {_dst_tx[0]} (IM:InvokeCommandRequest)\n'
                        f'\t<- Sending cluster ({_cluster_tx[0]}) command ({_cmd_tx[0]}) on endpoint {_ep_tx[0]}'))
        _cmd_send = _cmd_tx[0]  # _cmd_send is used for some special command response, TBD
    # # RX command response, not only for Commissioning steps
    # > Some ones without Endpoint: .* Cluster: .* Command .*
    if 'ReadCommissioningInfo' in msg:
        _commission_step = 'ReadCommissioningInfo'
        # This response is different from other response of commissioning steps, no command id and name to match
        _patt = f"NetworkCommissioning Features: (.*)\r\n.*finished commissioning step 'ReadCommissioningInfo'"
        _network_features = re.findall(_patt, msg)
        if _network_features:
            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                            f'\t-> NetworkCommissioning Features:  {_network_features[0]}'))
        _vid = re.findall('Cluster = 0x28,\r\n.*Attribute = 0x0000_0002,\r\n.*\}\r\n.*\n.*Data = (.*),', msg)
        _pid = re.findall('Cluster = 0x28,\r\n.*Attribute = 0x0000_0004,\r\n.*\}\r\n.*\n.*Data = (.*),', msg)
        if _vid and _pid:
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t->VendorID:{_vid[0]}'))
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t->ProductID:{_pid[0]}'))
    if "Performing next commissioning step 'AttestationVerification'" in msg:
        _commission_step = 'AttestationVerification'
        _patt = 'Verifying attestation\r\n.*CHIP:CTL: (.*)\r'
        _veri = re.findall(_patt, msg)
        if _veri:
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_veri[0]}'))
        if "Failed in verifying 'Attestation Information' command received from the device" in msg:
            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                            "\t-> Error on commissioning step 'AttestationVerification"))
            _err_code = re.findall(
                "'Attestation Information' command received from the device: err (.*). Look.*", msg)
            if _err_code:
                if _err_code[0] in AttestationVerificationResultEnum.keys():
                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                    f'\t\n-->> err {_err_code[0]}: '
                                    f'{AttestationVerificationResultEnum[_err_code[0]]} <<--\n'))
    if "Performing next commissioning step 'ValidateCSR'" in msg:
        _commission_step = 'ValidateCSR'
        _patt = "Performing next commissioning step 'ValidateCSR'\r\n.*CHIP:CTL: (.*)\r"
        _csr_veri = re.findall(_patt, msg)
        if _csr_veri:
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t->{_csr_veri[0]}'))
    # > Some ones with Endpoint: .* Cluster: .* Command .*
    _response_data = re.findall('Received Command Response Data, (.*)\r', msg)
    _recv_data = re.findall('Received Command Response Data .*\r\nReceived (.*)\r', msg)
    _recv_status = re.findall('Received Command Response Status for Endpoint.*Status=.*\r', msg)

    # > There may be 3 cases at least as bellow,
    # 'Received Command Response Data, .*\r\nEndpoint: .* Cluster: .* Command .*\r\n .*:{ '
    # 'Received Command Response Status for Endpoint.*Status=.*\r'
    # 'Received Command Response Data, Endpoint=0 Cluster=0x0000_003E Command=0x0000_0008'
    if len(_response_data) > 0 and len(_recv_status) == 0:  # without 'Status'
        _ep = re.findall('Endpoint=(.*) Cluster=.* Command=.*', _response_data[0])
        _clu_id = re.findall('Endpoint=.* Cluster=(.*) Command=.*', _response_data[0])
        _cm_id = re.findall('Endpoint=.* Cluster=.* Command=(.*)', _response_data[0])
        _cmd_id, _cmd_name = None, None
        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                        f'-->>Rx Cmd_Resp, '
                        f'Endpoint: {_ep[0]} Cluster: {_clu_id[0]} Command {_cm_id[0]}'))
        # Asert command_name, such as 'ScanNetworksResponse', or other command of commissioning steps
        if _clu_id and _cm_id:
            _cmd_id = '0x' + _clu_id[0].replace('0x0000_', '') + '_' + _cm_id[0].replace('0000_', '')
            if _cmd_id in cmds.keys():
                _cmd_name = cmds[_cmd_id]
            if not _cmd_name:
                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                f'-->> Unknown Command Response, does not parser yet'))
                return
        else:  # unknown _cmd_name, does not need to match anything
            return
        #  > commissioning steps as bellow.
        #   steps= ['ReadCommissioningInfo', 'ArmFailSafe', 'ConfigRegulatory', 'SendPAICertificateRequest',
        #     #     'SendPAICertificateRequest', 'SendDACCertificateRequest', 'SendAttestationRequest',
        #           'AttestationVerification', 'SendOpCertSigningRequest', 'ValidateCSR', 'GenerateNOCChain',
        #     #     'SendTrustedRootCert', 'SendNOC', 'WiFiNetworkSetup', 'FailsafeBeforeWiFiEnable',
        #     #     'WiFiNetworkEnable', 'FindOperational'  ]
        match _cmd_name:
            case 'ArmFailSafeResponse':  # Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0001
                _commission_step = 'ArmFailSafe'
                _patt = "Response Data, Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0001\r\n.*CHIP:CTL: (.*)\r"
                _af_res = re.findall(_patt, msg)
                if _af_res:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_af_res[0]}'))
            case 'SetRegulatoryConfigResponse':  # Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0003
                _commission_step = 'ConfigRegulatory'
                _patt = "Response Data, Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0003\r\n.*CHIP:CTL:(.*)\r"
                _setReg = re.findall(_patt, msg)
                if _setReg:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_setReg[0]}'))
            case 'CertificateChainResponse':  # Endpoint=0 Cluster=0x0000_003E Command=0x0000_0003
                # asert PAI/DAC by _commission_step
                if _commission_step == 'ConfigRegulatory' or _commission_step == 'ArmFailSafe':
                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                    f'\t-> Received certificate chain [PAI] from the device'))
                if _commission_step == 'SendPAICertificateRequest':
                    _commission_step = 'SendDACCertificateRequest'
                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                    f'\t-> Received certificate chain [DAC] from the device'))
                _commission_step = 'SendPAICertificateRequest'
            case 'AttestationResponse':  # Endpoint=0 Cluster=0x0000_003E Command=0x0000_0001
                _commission_step = 'SendAttestationRequest'
                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                f'\t-> Received Attestation Information from the device'))
            case 'CSRResponse':  # Endpoint=0 Cluster=0x0000_003E Command=0x0000_0005
                _commission_step = 'SendOpCertSigningRequest'
                _patt = 'Endpoint=0 Cluster=0x0000_003E Command=0x0000_0005\r\n.*CHIP:CTL: (.*)\r'
                _sign_req = re.findall(_patt, msg)
                if _sign_req:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_sign_req[0]}'))
            case 'NOCResponse':  # Endpoint=0 Cluster=0x0000_003E Command=0x0000_0008
                _patt = 'Response Data, Endpoint=0 Cluster=0x0000_003E Command=0x0000_0008\r\n.*CHIP:CTL: (.*)\r'
                _msg_returned = re.findall(_patt, msg)
                if _msg_returned:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_msg_returned[0]}'))
            case 'NetworkConfigResponse':  # Endpoint=0 Cluster=0x0000_0031 Command=0x0000_0005
                _patt = 'Response Data, Endpoint=0 Cluster=0x0000_0031 Command=0x0000_0005\r\n.*CHIP:CTL: (.*)\r'
                _nwk_cfg = re.findall(_patt, msg)
                if _nwk_cfg:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_nwk_cfg[0]}'))
            case 'ConnectNetworkResponse':  # Endpoint=0 Cluster=0x0000_0031 Command=0x0000_0007
                _patt = 'Response Data, Endpoint=0 Cluster=0x0000_0031 Command=0x0000_0007\r\n.*CHIP:CTL: (.*)\r'
                _nwk_status = re.findall(_patt, msg)
                if _nwk_status:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_nwk_status[0]}'))
            case 'CommissioningCompleteResponse':  # Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0005
                _patt = 'Response Data, Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0005\r\n.*CHIP:CTL: (.*)\r'
                _commissioning_cmp = re.findall(_patt, msg)
                if _commissioning_cmp:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'\t-> {_commissioning_cmp[0]}'))
            # other command response
            case 'ScanNetworksResponse':
                # potential problem for ScanNetWorksResponse:  there may be dozens of wifiScanResults, it is too long to
                # display in one message, but msg_parser can not handle two discontinuous messages
                _num = re.findall('wiFiScanResults: (.*) entries\r', msg)
                _ssid = re.findall('Ssid: (.*)\r', msg)
                _bssid = re.findall('Bssid: (.*)\r', msg)
                _security = re.findall('Security: (.*)\r', msg)
                _ch = re.findall('Channel: (.*)\r', msg)
                _band = re.findall('WiFiBand: (.*)\r', msg)
                _rssi = re.findall('Rssi: (.*)\r', msg)

                if _num:
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'>\t>>Wi-Fi ScanResults: {_num[0]}'))
                    if _num[0] != '0':
                        for i in range(0, int(_num[0])):
                            try:
                                # for ASCII symbol SSID
                                _ssid_str = str(bytes.fromhex(_ssid[i]))[2:-1]
                                # TODO: for None-ASCII symbol SSID
                                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                f'\tSSID: {_ssid_str}\tBSSID: {_bssid[i]}\t'
                                                f'tRSSI：{_rssi[i]}\tSecurity: {_security[i]}'))

                            except Exception as E:
                                if 'list index out of range' in str(E):
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    '>> ** Error: Some wifiScanResults is return in another message '
                                                    'but can not be handled by msg_parser**<<'))
                                    return
                        return
            case _:
                pass
    if len(_recv_status) > 0: # Received Command Response Status for Endpoint.*Status=.*\r
        _ep = re.findall('Endpoint=(.*) Cluster=.* Command=.*', _recv_status[0])
        _clu_id = re.findall('Endpoint=.* Cluster=(.*) Command=.*', _recv_status[0])
        _cm_id = re.findall('Endpoint=.* Cluster=.* Command=(.*) Status=', _recv_status[0])
        _status_code = re.findall('Endpoint=.* Cluster=.* Command=.* Status=(.*)\r', _recv_status[0])
        _cmd_id, _cmd_name = None, None
        # this type of response matches the commands such as 'SendTrustedRootCert'
        if _clu_id and _cm_id:
            _cmd_id = '0x' + _clu_id[0].replace('0x0000_', '') + '_' + _cm_id[0].replace('0000_', '')
            if _cmd_id in cmds.keys():
                _cmd_name = cmds[_cmd_id]
            if not _cmd_name:
                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                f'-->> {_cmd_id}:Unknown Command Response(with status code), does not parser yet'))
                return
        else:  # unknown _cmd_name, does not need to match anything
            return
        match _cmd_name:
            case 'AddTrustedRootCertificate':  # Endpoint=0 Cluster=0x0000_003E Command=0x0000_000B Status=0x0
                if _status_code:
                    if _status_code[0] == '0x0':
                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                        '\t->Device confirmed that it has received the root certificate'))
                    elif _status_code[0] in cmd_res_status.keys():
                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                f'\t->"AddTrustedRootCertificate" command response {cmd_res_status[_status_code[0]]}'))
            case 'OpenCommissioningWindow':  # Endpoint=0 Cluster=0x0000_003C Command=0x0000_0000 Status=0x0
                _manual_pair_code = re.findall('Manual pairing code: \[(.*)\]', msg)
                _qrcode = re.findall('SetupQRCode: \[(.*)\]', msg)
                if len(_manual_pair_code) > 0 and len(_qrcode) > 0:
                    dispatcher.put((ui.fabric_manual_pairing_code, 'QLineEdit.setText', _manual_pair_code[0]))
                    dispatcher.put((ui.fabric_qr_code, 'QLineEdit.setText', _qrcode[0]))
                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'OCW_QR-Code: {_qrcode}\n'
                                                                       f'OCW_ManualPairingCode: {_manual_pair_code}'))
            case _:
                pass
    # 5.Attribute Read Request and Response parse
    try:
        # Sending ReadAttribute to
        _read_req = re.findall('Sending ReadAttribute to:\r\n.*TOO: 	(.*)\r', msg)
        _dest_id = re.findall('Sending Read Request\r\n.*Msg TX to(.*)\[', msg)
        _suppress_res = re.findall('SuppressResponse = (.*),', msg)
        if len(_read_req) > 0 and len(_dest_id) > 0:
            dispatcher.put((ui.keynote, 'QTextBrowser.append', f'<<-- Sending ReadAttribute to '
                                                                f'{_dest_id[0]}: \n\t<- {_read_req[0]}'))
        # parse the cluster id and attribute id , couple the ids to 0xXXXX_0xXXXX, which
        # 'Endpoint: 1 Cluster: 0x0000_0006 Attribute 0x0000_0000 DataVersion'
        # TODO: it only works for single attribute mode, need to iteration for wildcard 0xFFFFFFFF mode
        attrib_res = re.findall('Endpoint: .* Cluster: .* Attribute .* DataVersion', msg)
        if len(attrib_res) > 0:
            # Iterates the list and parses each attribute
            for i in range(0, len(attrib_res)):
                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                            f'\t-> {attrib_res[i].replace("DataVersion", "")}'))
                _ep = re.findall('Endpoint: (.*) Cluster: .* Attribute .* DataVersion', attrib_res[i])
                _cluster = re.findall('Endpoint: .* Cluster: (.*) Attribute .* DataVersion', attrib_res[i])
                _attrib = re.findall('Endpoint: .* Cluster: .* Attribute (.*) DataVersion', attrib_res[i])
                if _ep and _cluster and _attrib:
                    _endpoint, _cluster_id, _attrib_id = _ep[0], _cluster[0].replace('0000_', ''), \
                        _attrib[0].replace('0000_', '')
                    _unique_id = _cluster_id + '_' + _attrib_id
                    if _unique_id in attributes.keys():
                        _attrib_name = attributes[_unique_id]
                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                    f'\t-> AttributeName:{_attrib_name}'))
                        pattern = 'Endpoint: %s Cluster: %s Attribute %s DataVersion.*\r\n.*%s: (.*)\r' % (
                            _ep[0], _cluster[0], _attrib[0], _attrib_name)
                        re.compile(pattern)
                        """
                        # most attribute values can be displayed in a single line, while some special ones require 
                        # multiple lines,  or some ones need to be translated to readable format. Each case 
                        # statement corresponds to an attribute that requires special handling.
                        """
                        match _attrib_name:
                            case 'CapabilityMinima':
                                # displayed in 4 lines
                                pat = 'CapabilityMinima: \{\r\n.*CHIP:TOO:(.*)\r\n.*CHIP:TOO:(.*)\r\n.*\}\r'
                                re.compile(pat)
                                target = re.findall(pat, msg)
                                # CapabilityMinima_refindall:[('     CaseSessionsPerFabric: 3', '
                                # SubscriptionsPerFabric: 3')]
                                if len(target) > 0:
                                    value = ('\t-> CapabilityMinima: {\n\t\t' + target[0][0] + '\n\t\t' +
                                             target[0][1] + '\n\t\t}')
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', f'{value}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'AttributeList':
                                # need to show all listed items
                                number = re.findall('AttributeList: (.*) entries\r', msg)
                                if len(number) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> AttributeList: {number[0]} entries'))
                                    temp = re.findall(f'AttributeList: {number[0]} entries.*',
                                                      msg, re.DOTALL)
                                    if len(temp) > 0:
                                        for n in range(1, int(number[0]) + 1):
                                            ids = re.findall(f'\[{str(n)}\]: (.*)\r\n', temp[0])
                                            if len(ids) > 0:
                                                _attrib_id_hex = '0x' + str('{:04x}'.format(int(ids[0]))).upper()
                                                _unique_id = _cluster_id + '_' + _attrib_id_hex
                                                if _unique_id in attributes.keys():
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t-> {_unique_id}: {attributes[_unique_id]}'))
                                                else:
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t-> {_unique_id}:Unknown Attribute'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'DeviceTypeList':
                                dtls_num = re.findall('DeviceTypeList: (.*) entries\r', msg)
                                if len(dtls_num) > 0:
                                    if dtls_num[0] == '1':
                                        dtl = re.findall(
                                        'DeviceTypeList: 1 entries\r\n.*\[1\]:.*\r\n.*DeviceType: (.*)\r', msg)
                                        if len(dtl) > 0:
                                            dt = '0x' + '{:04x}'.format(int(dtl[0])).upper()
                                            if dt in app_dev_types.keys():
                                                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                f'\t\t-> {_attrib_name}:{dt} _ {app_dev_types[dt]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'ServerList':
                                ser_num = re.findall('ServerList: (.*) entries', msg)
                                if len(ser_num) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> ServerList at Endpoint {_endpoint}: {ser_num[0]} entries'))
                                    temp = re.findall(f'ServerList: {ser_num[0]} entries.*', msg, re.DOTALL)
                                    if len(temp) > 0:
                                        for m in range(1, int(ser_num[0]) + 1):
                                            ser = re.findall(f'\[{m}\]: (.*)\r\n', temp[0])
                                            if len(ser) > 0:
                                                cluster_id = '0x' + '{:04x}'.format(int(ser[0])).upper()
                                                if cluster_id in clusters.keys():
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t-> {cluster_id}_{clusters[cluster_id]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'ClientList':
                                cl_num = re.findall('ClientList: (.*) entries', msg)
                                if len(cl_num) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                f'\t->ClientList at Endpoint {_endpoint}: '
                                                f'{cl_num[0]} entries'))
                                    temp = re.findall(f'ClientList: {cl_num[0]} entries.*', msg, re.DOTALL)
                                    if len(temp) > 0:
                                        for i in range(1, int(cl_num[0]) + 1):
                                            ser = re.findall(f'\[{i}\]: (.*)\r\n', temp[0])
                                            if len(ser) > 0:
                                                cluster_id = '0x' + '{:04x}'.format(int(ser[0])).upper()
                                                if cluster_id in clusters.keys():
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t->{cluster_id}_{clusters[cluster_id]}'))
                                dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'AcceptedCommandList':
                                num = re.findall('AcceptedCommandList: (.*) entries\r', msg)
                                if num:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> Cluster_{_cluster_id} AcceptedCommandList: '
                                                    f'{num[0]} entries'))
                                    temp = re.findall(f'AcceptedCommandList: {num[0]} entries.*',
                                                      msg, re.DOTALL)
                                    if len(temp) > 0:
                                        for i in range(1, int(num[0]) + 1):
                                            target = re.findall(f'\[{i}\]: (.*)', temp[0])
                                            if len(target) > 0:
                                                cmd_id = _cluster_id + '_' + '0x' + '{:04x}'.format(
                                                    int(target[0])).upper()
                                                if cmd_id in cmds.keys():
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t-> {cmd_id}_{cmds[cmd_id]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'GeneratedCommandList':
                                num = re.findall('GeneratedCommandList: (.*) entries\r', msg)
                                if num:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> Cluster_{_cluster_id} GeneratedCommandList: '
                                                    f'{num[0]} entries'))
                                    temp = re.findall(f'GeneratedCommandList: {num[0]} entries.*',
                                                      msg, re.DOTALL)
                                    if len(temp) > 0:
                                        for i in range(1, int(num[0]) + 1):
                                            target = re.findall(f'\[{i}\]: (.*)', temp[0])
                                            if target:
                                                cmd_id = _cluster_id + '_' + '0x' + '{:04x}'.format(
                                                    int(target[0])).upper()
                                                if cmd_id in cmds.keys():
                                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                                    f'\t\t-> {cmd_id}_{cmds[cmd_id]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append','\n'))
                            case 'NetworkInterfaces':
                                entries = re.findall('NetworkInterfaces: (.*) entries', msg)
                                if entries:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> NetworkInterfaces: {entries[0]} entries'))
                                interface_name = re.findall('Name: (.*)\r', msg)
                                ip_addresses = re.findall('CHIP:TOO:         \[1\]: (.*)\r', msg)
                                # Sample of ip_address: ['C0A80801', 'C0A87D15', 'FE800000000000000A3A8DFFFE40E9D0']
                                # ip_address[0]: AP interface, ip_address[1]: STA_interface
                                # 十六进制字符串转字节码，按字节读取即为十进制数，将十六进制字符按字节读取转成 十进制格式的IPv4地址
                                for addr in ip_addresses:
                                    if len(addr) != 8:
                                        continue
                                    addr = bytes.fromhex(addr)
                                    ip_addr = ''
                                    for i in addr:
                                        ip_addr += str(i) + '.'
                                    ip_addr = ip_addr[:-1]
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t->IPv4Addresses: {ip_addr}'))
                                dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'SecurityType':
                                security_type = re.findall('SecurityType: (.*)\r', msg)
                                if len(security_type) > 0:
                                    if security_type[0] == 'null':
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        '\t-> SecurityType: null\n'))
                                    else:
                                        if isinstance(security_type[0], int):
                                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                            '\t-> dataType of security_type is int'))
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        f'\t-> SecurityTpe: {str(security_type[0])} --> '
                                                        f'{security_types[str(security_type[0])]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'WiFiVersion':
                                WiFiVersion = re.findall(r'WiFiVersion: (.*)\r', msg)
                                wifi_versions = {'0': '802.11a', '1': '802.11b', '3': '802.11g',
                                                 '4': '802.11ac', '5': '802.11ax', '6': '802.11ah'}
                                if len(WiFiVersion) > 0:
                                    if WiFiVersion[0] == 'null':
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        '\t-> WiFiVersion: null'))
                                    else:
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        f'\t-> WiFiVersion: {WiFiVersion[0]}->'
                                                        f'{wifi_versions[WiFiVersion[0]]}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'LastNetworkID':
                                target = re.findall('LastNetworkID: (.*)\r', msg)
                                if target:
                                    if target[0] == 'null':
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        '\t->LastNetworkID: null'))
                                    else:
                                        ssid = bytes.fromhex(target[0])
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        f'\t-> LastNetworkID: {ssid}'))
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'WindowStatus':
                                CommissioningWindowStatusEnum = {
                                    '0': 'WindowNotOpen <-> Commissioning window not open',
                                    '1': 'EnhancedWindowOpen <-> An EnhancedCommissioning Method window is open',
                                    '2': 'BasicWindowOpen <-> A Basic Commissioning Method window is open'
                                }
                                sta = re.findall('WindowStatus: (.*)\r', msg)
                                if sta:
                                    window_status = sta[0]
                                    if window_status in CommissioningWindowStatusEnum.keys():
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                        f'\t-> WindowsStatus: {window_status}\n'
                                                        f'{CommissioningWindowStatusEnum[window_status]}'))
                                    if sta[0] == '0':
                                        dispatcher.put((ui.window_timeout, 'QLineEdit.setText', '0'))
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                                        # ui.open_window.setStyleSheet('background-color: rgb(250, 250, 250);')
                            case 'Fabrics':
                                fab_num = re.findall('Fabrics: (.*) entries\r', msg)
                                root_pub_key = re.findall('RootPublicKey: (.*)\r', msg)
                                VendorId = re.findall('VendorID: (.*)\r', msg)
                                FabricId = re.findall('FabricID: (.*)\r', msg)
                                NodeId = re.findall('NodeID: (.*)\r', msg)
                                Label = re.findall('Label: (.*)\r', msg)
                                FabricIndex = re.findall('FabricIndex: (.*)\r', msg)
                                if fab_num and FabricIndex and NodeId and FabricId and VendorId:
                                    if int(fab_num[0]) == len(FabricIndex):  # entries number == fabricIndex number
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t\tFabrics: {fab_num[0]} entries'))
                                        for x in range(0, int(fab_num[0])):
                                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                            f'\t\t[{str(x + 1)}]:\n\t\tRootPublicKey:'
                                                            + root_pub_key[x] +
                                                            '\n\t\tVendorId:' + VendorId[x] +
                                                            '\n\t\tFabricId:' + FabricId[x] +
                                                            '\n\t\tLabel:' + Label[x] +
                                                            '\n\t\tFabricIndex:' + FabricIndex[x]))
                                        dispatcher.put((ui.keynote, 'QTextBrowser.append', '\n'))
                            case 'UpdateState':
                                value = re.findall(pattern, msg)
                                if len(value) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> {_attrib_name}: {value[0]}'))
                                if value[0] in UpdateStateEnum.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t->{UpdateStateEnum[value[0]]}\n'))
                            case 'HourFormat':
                                value = re.findall(pattern, msg)
                                if len(value) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> {_attrib_name}: {value[0]}'))
                                if value[0] in HourFormatEnum.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t->{HourFormatEnum[value[0]]}\n'))
                            case 'ActiveCalendarType':
                                value = re.findall(pattern, msg)
                                if len(value) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> {_attrib_name}: {value[0]}'))
                                if value[0] in CalendarTypeEnum.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            f'\t->Dates conveyed using the {CalendarTypeEnum[value[0]]} calendar\n'))
                            case '':
                                pass
                            case _:  # default action
                                value = re.findall(pattern, msg)
                                if len(value) > 0:
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'\t-> {_attrib_name}: {value[0]}\n'))
        if len(attrib_res) == 0 and len(_suppress_res) > 0:
            if _suppress_res[0] == 'true':
                _prompt = ('\t--> a Report Data action that initiates a Report transaction that conveys an empty list '
                           'of AttributeReports and EventReports received')
                dispatcher.put((ui.keynote, 'QTextBrowser.append', _prompt))
    except Exception as E:
        dispatcher.put((ui.keynote, 'QTextBrowser.append', traceback.format_exc()))
        dispatcher.put((ui.keynote, 'QTextBrowser.append', str(E)))

    # 6.Cluster Ctrl Command (such as onoff/lvl/cc) Response parse
    # Exa: 'Received Command Response Status for Endpoint=0 Cluster=0x0000_003C Command=0x0000_0000 Status=0x1'
    try:
        # RX Msg
        pat = 'Received Command Response Status for Endpoint=.*Cluster=.*Command=.*Status=.*'
        re.compile(pat)
        cmd_res = re.findall(pat, msg)
        if cmd_res:
            _ep = re.findall('Endpoint=(.*) Cluster', cmd_res[0])
            _cluster = re.findall('Cluster=(.*) Command', cmd_res[0])
            _cmd = re.findall('Command=(.*) Status', cmd_res[0])
            _sta = re.findall(r'Command=.*Status=(.*)\r', cmd_res[0])

            if _ep and _cluster and _cmd and _sta:
                _endpoint, _cluster_id = _ep[0], _cluster[0].replace('0000_', '')
                _cmd_id, _status_code = _cluster_id + '_' + _cmd[0].replace('0000_', ''), _sta[0]

            if _cmd_id in cmds:
                _cmd_name = cmds[_cmd_id]
                match _cmd_name:
                    # 0x003C
                    case 'OpenCommissioningWindow':  # 0x003C_0x0000
                        if _status_code == '0x0':
                            # Successfully opened pairing window on the device
                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            'Successfully opened pairing window on the device'))
                        elif _status_code == '0x1':
                            # Failed to open pairing window on the device
                            sta_code = re.findall('Failed to open pairing window .* error: (.*)\r', msg)
                            if sta_code:
                                if sta_code[0] in AdministratorCommissioningStatusCode.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    '\t-->#Failed to open pairing window on the device.\n'
                                                    f'\t-->Status IM Error: Cluster-specific error: '
                                                    f'{sta_code[0]}\n '
                                                    f'\t-->#{AdministratorCommissioningStatusCode[sta_code[0]]}'
                                                    ))
                            else:
                                dispatcher.put((ui.keynote, 'QTextBrowser.append', f'{_status_code}'))
                    case 'OpenBasicCommissioningWindow':  # 0x003C_0x0001
                        if _status_code == '0x0':
                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            'Successfully opened BasicCommissioningWindow(BCM) on the device'))
                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            '[Please use the origin QR-Code or manualPairingCode for commissioning,'
                                            'you can get it from the label of dut]'))
                        elif _status_code == '0x1':
                            # Failed to open pairing window on the device
                            sta_code = re.findall('Error: IM Error 0x00000602: Cluster-specific error: (.*)\r',
                                                  msg)
                            if sta_code:
                                if sta_code[0] in AdministratorCommissioningStatusCode.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    'Failed to open pairing window on the device.'
                                                    f'Status IM Error.*: Cluster-specific error: '
                                                    f'{sta_code[0]}\n '
                                                    f'{AdministratorCommissioningStatusCode[sta_code[0]]}'))
                            elif _status_code == '0xc6':
                                dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                'OBCW command NEEDS_TIMED_INTERACTION'))
                                return
                            else:
                                dispatcher.put((ui.keynote, 'QTextBrowser.append',f'{_status_code}'))
                    case 'RevokeCommissioning':  # 0x003C_0x0002
                        # TODO, need to be re-build this
                        # reset self.ocw_timer, reset the background color of Open-Window button,
                        # stop window_timeout sub-thread
                        if _status_code == '0x0':
                            _ocw_timer, _revoked = '0', True
                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            '\t<< OpenCommissioningWindow (OCW or OBCW) Command successful Revoked >>'))
                        if _status_code == '0x1':
                            cluster_status = re.findall('Error: M Error.*Cluster-specific error: (.*)\r', msg)
                            if cluster_status:
                                if cluster_status[0] in AdministratorCommissioningStatusCode.keys():
                                    dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                                    f'{AdministratorCommissioningStatusCode[cluster_status[0]]}'))
                        if _status_code == '0xc6':
                            dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                            'OBCW Revoke Command NEEDS_TIMED_INTERACTION'))
                    case _:  # default action
                        dispatcher.put((ui.keynote, 'QTextBrowser.append',
                                        f'\t-> {cmds[_cmd_id]} response ->> '
                                        f'{cmd_res_status[_status_code]}'))
    except Exception as E:
        dispatcher.put((ui.keynote, 'QTextBrowser.append', str(E)))
        dispatcher.put((ui.keynote, 'QTextBrowser.append', traceback.format_exc()))

    return _commission_step
